/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> logging.c </file>
  <brief>
    Test the logging APIs in ULS-LF.
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, 2011.
  </author>
*/

#include "uls.h"
#include "uls_log.h"
#include "sample_lex.h"

char *progname;
char *config_name;
char *input_file;
int opt_mode;
int opt_verbose;

uls_lex_t *sample_lex;

#include <time.h>
#include <locale.h>
#include <getopt.h>
#include <pthread.h>

static void usage(void)
{
	err_log("%s v1.0", progname);
	err_log("  Demonstrates the use of ULS-LF, the ULS Logging Framework.");
	err_log(" Just type");
	err_log("  %s input1.txt", progname);
	exit(1);
}

static int
parse_options(int argc, char* argv[])
{
	int   opt;

	while ((opt=getopt(argc, argv, "m:vh")) != -1) {
		switch (opt) {
		case 'm':
			opt_mode = atoi(optarg);
			break;

		case 'v':
			++opt_verbose;
			break;

		case 'h':
			usage();
			break;

		default:
			err_log("undefined option -%c", opt);
			return -1;
			break;
		}
	}

	return optind;
}

static int
fmtproc_tabs(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	int i, len, buf_siz;
	char buf[16];

	buf_siz = sizeof(buf);
	memset(buf, '\t', buf_siz);

	len = (int) va_arg(ctx->args, int);

	for (i=0; i<len/buf_siz; i++)
		puts_proc(ctx->x_dat, buf, buf_siz);

	if ((i=len % buf_siz) > 0)
		puts_proc(ctx->x_dat, buf, i);

	return len;
}

static int
fmtproc_date(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	char buf[32];
	int len;
	struct tm *today;
	time_t curtim;
	
	curtim = time(NULL);
	today = localtime(&curtim);

	len = uls_snprintf(buf, sizeof(buf), "%4d-%02d-%02d",
		1900 + today->tm_year, today->tm_mon + 1, today->tm_mday);
	puts_proc(ctx->x_dat, buf, len);

	return len;
}

void
test_sprintf(void)
{
	char buff[128];
	int len, i, ui, width, tabsiz;
	long long ii;
	unsigned long long uii;
	char *str = "hello";
	double x;
	long double xx;

	len = uls_snprintf(buff, sizeof(buff), "");
	printf("buff = '%s'\n", buff);

	len = uls_snprintf(buff, sizeof(buff), "'%s' '%8s' '%-8s'", str, str, str);
	printf("buff = '%s'\n", buff);

	buff[0] = 'X'; buff[1] = 'Y'; buff[2] = 'Z';
	len = uls_snprintf(buff, sizeof(buff), "%c he-%c-llo %c",
		buff[0], buff[1], buff[2]);
	printf("buff = '%s'\n", buff);

	i = 2008;
	len = uls_snprintf(buff, sizeof(buff), "hello 0x%p world", i);
	printf("buff = %s'(len=%d)\n", buff, len);

	i = 7;
	len = uls_snprintf(buff, sizeof(buff), "%+5d", i);
	printf("buff = '%s'(len=%d)\n", buff, len);

	i = -9;
	len = uls_snprintf(buff, sizeof(buff), "%5d<h?llo>", i);
	printf("buff = '%s'(len=%d)\n", buff, len);

	ui = 7;
	len = uls_snprintf(buff, sizeof(buff), "%-3u", ui);
	printf("buff = '%s'(len=%d)\n", buff, len);

	ui = 2008;
	ii = -35;
	len = uls_snprintf(buff, sizeof(buff), "%Ld", ii);
	printf("buff = '%s'(len=%d)\n", buff, len);

	uii = (unsigned long long) ii;
	len = uls_snprintf(buff, sizeof(buff), "%Lu+%u", uii, ui);
	printf("buff = '%s'(len=%d)\n", buff, len);

	i = 2008; width = 7;
	len = uls_snprintf(buff, sizeof(buff), "%-12d", i);
	printf("buff = '%s'\n", buff);

	i = -2011;
	tabsiz = 2;
	len = uls_snprintf(buff, sizeof(buff), "[%m] ---%t---", tabsiz);
//	printf("buff = '%s', len = %d\n", buff, len);

	x = 314159.2653589;
	// use %f for printing 'double'
	len = uls_snprintf(buff, sizeof(buff), "%.10f %.10e %.10g", x, x, x);
	// NOTICE: %f will dismiss the digits below the precision, here 10, instead of rounding it up.
	printf("buff = '%s'(len=%d)\n", buff, len);

	xx = -0.00031415926535897;
	// use %lf for printing 'long double' instead of %Lf
	len = uls_snprintf(buff, sizeof(buff), "%.10lf %.10le %.10lg", xx, xx, xx);
	// NOTICE: %lf will dismiss the digits below the precision, here 10, instead of rounding it up.
	printf("buff = '%s'(len=%d)\n", buff, len);
}

void sample_log(char* fmt, ...)
{
	va_list	args;

	va_start(args, fmt);
	uls_vlog(sample_lex, fmt, args);
	va_end(args);
}

void
test_log(uls_lex_t* uls)
{
	unsigned int ui;
	long long ii;
	int i;
	int tok;

	i = 7;
	err_log("mesg = '%+5d'", i);

	i = -9;
	err_log("mesg = '%5d<?ello>'", i);

	ui = 7;
	err_log("mesg = '%-3u'", ui);

	i = 2008;
	tok = uls_gettok(uls);
	uls_log(uls, "buff = '%-12d, tok=%d:'%s'", i, tok, uls_lexeme_text(uls));

	ui = 2008;
	ii = -35;

	tok = uls_gettok(uls);
	uls_log(uls, "buff = '%Ld', tok=%d: %k", ii, tok);
	uls_wlog(uls, L"buff = '%-12d, tok=%d:'%s'", i, tok, uls_lexeme_text(uls));
	uls_wlog(uls, L"buff = '%Ld', tok=%d: %k at %w", ii, tok);

	tok = uls_gettok(uls);
	sample_log("buff = '%Ld+%u', tok=%d: at %w", ii, ui, tok);
}

void *thr_task(void *param)
{
	int tid = (int) param;
	int   i;

	for (i = 0; i < 10; i++) {
		if (i % 2 == 0)
			uls_log(sample_lex, "<tid=%d> says hello #%d", tid, i);
		else
			err_log("\t<tid=%d> says hello #%d", tid, i);
	}

	return NULL;
}

#define N_THREADS 8

void
test_log_threads(sample_lex)
{
	pthread_attr_t	thread_attr;
	pthread_t tid[N_THREADS];
	int i, n_thr_ary = N_THREADS;
	
	pthread_attr_init(&thread_attr);

	for (i=0; i<n_thr_ary; i++) {
		pthread_create(&tid[i], &thread_attr, thr_task, (void *) i);
	}
	pthread_attr_destroy(&thread_attr);

	for (i=0; i<n_thr_ary; i++) {
		pthread_join(tid[i], NULL);
	}
}

int tst_print(uls_lf_t* uls_lf, uls_lf_char_t* fmt, ...)
{
	va_list	args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(uls_lf, stdout, uls_lf_puts_file, NULL, fmt, args);
	va_end(args);

	return len;
}

void
test_uls_lf_instance(void)
{
	uls_lf_t  tst_lf2;
	int i;

	uls_lf_init(&tst_lf2, NULL);

	for (i=0; i<4; i++) {
		tst_print(&tst_lf2, "\thello uls-lf %d\n", i);
	}

	uls_lf_deinit(&tst_lf2);
}

int
main(int argc, char* argv[])
{
	int i0;

	progname = uls_filename(argv[0]);
	uls_lf_register_convspec(NULL, "t", fmtproc_tabs);
	uls_lf_register_convspec(NULL, "m", fmtproc_date);

	if (!setlocale(LC_CTYPE, "")) {
		err_log("fail to setlocale!");
		usage();
	}

	if ((i0=parse_options(argc, argv)) < 0) {
		err_log("improper use of options");
		usage();
	}

	config_name = "sample.ulc";
	input_file = "input1.txt";

	if ((sample_lex = uls_create(config_name)) == NULL) {
		err_log("can't create uls-lexer");
		return -1;
	}

	if (uls_set_file(sample_lex, input_file, 0) < 0) {
		err_log("can't find input file or error!");
		return -1;
	}

	if (opt_mode==0) {
		test_sprintf();
		test_log(sample_lex);
	} else if (opt_mode==1) {
		test_log_threads(sample_lex);
	} else if (opt_mode==2) {
		test_uls_lf_instance();
	}

	uls_destroy(sample_lex);

	return 0;
}
